// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/pepper/pepper_file_ref_host.h"

#include <string>

#include "content/browser/renderer_host/pepper/pepper_external_file_ref_backend.h"
#include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
#include "content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_file_info.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/file_ref_util.h"
#include "storage/browser/fileapi/file_permission_policy.h"

using ppapi::host::ResourceHost;

namespace content {

PepperFileRefBackend::~PepperFileRefBackend() { }

PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host,
    PP_Instance instance,
    PP_Resource resource,
    PP_Resource file_system,
    const std::string& path)
    : ResourceHost(host->GetPpapiHost(), instance, resource)
    , host_(host)
    , fs_type_(PP_FILESYSTEMTYPE_INVALID)
{
    if (!ppapi::IsValidInternalPath(path))
        return;

    int render_process_id;
    int unused;
    if (!host->GetRenderFrameIDsForInstance(
            instance, &render_process_id, &unused)) {
        return;
    }

    ResourceHost* fs_resource_host = host->GetPpapiHost()->GetResourceHost(file_system);
    if (fs_resource_host == NULL) {
        DLOG(ERROR) << "Couldn't find FileSystem host: " << resource
                    << " path: " << path;
        return;
    }

    if (!fs_resource_host->IsFileSystemHost()) {
        DLOG(ERROR) << "Filesystem PP_Resource is not PepperFileSystemBrowserHost";
        return;
    }

    PepperFileSystemBrowserHost* file_system_host = static_cast<PepperFileSystemBrowserHost*>(fs_resource_host);
    file_system_host_ = file_system_host->AsWeakPtr();
    fs_type_ = file_system_host->GetType();
    if ((fs_type_ != PP_FILESYSTEMTYPE_LOCALPERSISTENT) && (fs_type_ != PP_FILESYSTEMTYPE_LOCALTEMPORARY) && (fs_type_ != PP_FILESYSTEMTYPE_EXTERNAL) && (fs_type_ != PP_FILESYSTEMTYPE_ISOLATED)) {
        DLOG(ERROR) << "Unsupported filesystem type: " << fs_type_;
        return;
    }
    if ((fs_type_ == PP_FILESYSTEMTYPE_EXTERNAL) && (!file_system_host->GetRootUrl().is_valid())) {
        DLOG(ERROR) << "Native external filesystems are not supported by this "
                    << "constructor.";
        return;
    }

    backend_.reset(new PepperInternalFileRefBackend(host->GetPpapiHost(),
        render_process_id,
        file_system_host->AsWeakPtr(),
        path));
}

PepperFileRefHost::PepperFileRefHost(BrowserPpapiHost* host,
    PP_Instance instance,
    PP_Resource resource,
    const base::FilePath& external_path)
    : ResourceHost(host->GetPpapiHost(), instance, resource)
    , host_(host)
    , fs_type_(PP_FILESYSTEMTYPE_EXTERNAL)
{
    if (!ppapi::IsValidExternalPath(external_path))
        return;

    int render_process_id;
    int unused;
    if (!host->GetRenderFrameIDsForInstance(
            instance, &render_process_id, &unused)) {
        return;
    }

    backend_.reset(new PepperExternalFileRefBackend(
        host->GetPpapiHost(), render_process_id, external_path));
}

PepperFileRefHost::~PepperFileRefHost() { }

bool PepperFileRefHost::IsFileRefHost() { return true; }

PP_FileSystemType PepperFileRefHost::GetFileSystemType() const
{
    return fs_type_;
}

storage::FileSystemURL PepperFileRefHost::GetFileSystemURL() const
{
    if (backend_)
        return backend_->GetFileSystemURL();
    return storage::FileSystemURL();
}

base::FilePath PepperFileRefHost::GetExternalFilePath() const
{
    if (backend_)
        return backend_->GetExternalFilePath();
    return base::FilePath();
}

base::WeakPtr<PepperFileSystemBrowserHost>
PepperFileRefHost::GetFileSystemHost() const
{
    return file_system_host_;
}

int32_t PepperFileRefHost::CanRead() const
{
    if (backend_)
        return backend_->CanRead();
    return PP_ERROR_FAILED;
}

int32_t PepperFileRefHost::CanWrite() const
{
    if (backend_)
        return backend_->CanWrite();
    return PP_ERROR_FAILED;
}

int32_t PepperFileRefHost::CanCreate() const
{
    if (backend_)
        return backend_->CanCreate();
    return PP_ERROR_FAILED;
}

int32_t PepperFileRefHost::CanReadWrite() const
{
    if (backend_)
        return backend_->CanReadWrite();
    return PP_ERROR_FAILED;
}

int32_t PepperFileRefHost::OnResourceMessageReceived(
    const IPC::Message& msg,
    ppapi::host::HostMessageContext* context)
{
    if (!backend_)
        return PP_ERROR_FAILED;

    PPAPI_BEGIN_MESSAGE_MAP(PepperFileRefHost, msg)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_MakeDirectory,
        OnMakeDirectory)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_Touch, OnTouch)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_Delete, OnDelete)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileRef_Rename, OnRename)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_Query, OnQuery)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
        PpapiHostMsg_FileRef_ReadDirectoryEntries, OnReadDirectoryEntries)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileRef_GetAbsolutePath,
        OnGetAbsolutePath)
    PPAPI_END_MESSAGE_MAP()
    return PP_ERROR_FAILED;
}

int32_t PepperFileRefHost::OnMakeDirectory(
    ppapi::host::HostMessageContext* context,
    int32_t make_directory_flags)
{
    int32_t rv = CanCreate();
    if (rv != PP_OK)
        return rv;
    return backend_->MakeDirectory(context->MakeReplyMessageContext(),
        make_directory_flags);
}

int32_t PepperFileRefHost::OnTouch(ppapi::host::HostMessageContext* context,
    PP_Time last_access_time,
    PP_Time last_modified_time)
{
    // TODO(teravest): Change this to be kWriteFilePermissions here and in
    // fileapi_message_filter.
    int32_t rv = CanCreate();
    if (rv != PP_OK)
        return rv;
    return backend_->Touch(
        context->MakeReplyMessageContext(), last_access_time, last_modified_time);
}

int32_t PepperFileRefHost::OnDelete(ppapi::host::HostMessageContext* context)
{
    int32_t rv = CanWrite();
    if (rv != PP_OK)
        return rv;
    return backend_->Delete(context->MakeReplyMessageContext());
}

int32_t PepperFileRefHost::OnRename(ppapi::host::HostMessageContext* context,
    PP_Resource new_file_ref)
{
    int32_t rv = CanReadWrite();
    if (rv != PP_OK)
        return rv;

    ResourceHost* resource_host = host_->GetPpapiHost()->GetResourceHost(new_file_ref);
    if (!resource_host)
        return PP_ERROR_BADRESOURCE;

    PepperFileRefHost* file_ref_host = NULL;
    if (resource_host->IsFileRefHost())
        file_ref_host = static_cast<PepperFileRefHost*>(resource_host);
    if (!file_ref_host)
        return PP_ERROR_BADRESOURCE;

    rv = file_ref_host->CanCreate();
    if (rv != PP_OK)
        return rv;

    return backend_->Rename(context->MakeReplyMessageContext(), file_ref_host);
}

int32_t PepperFileRefHost::OnQuery(ppapi::host::HostMessageContext* context)
{
    int32_t rv = CanRead();
    if (rv != PP_OK)
        return rv;
    return backend_->Query(context->MakeReplyMessageContext());
}

int32_t PepperFileRefHost::OnReadDirectoryEntries(
    ppapi::host::HostMessageContext* context)
{
    int32_t rv = CanRead();
    if (rv != PP_OK)
        return rv;
    return backend_->ReadDirectoryEntries(context->MakeReplyMessageContext());
}

int32_t PepperFileRefHost::OnGetAbsolutePath(
    ppapi::host::HostMessageContext* context)
{
    if (!host_->GetPpapiHost()->permissions().HasPermission(
            ppapi::PERMISSION_PRIVATE))
        return PP_ERROR_NOACCESS;
    return backend_->GetAbsolutePath(context->MakeReplyMessageContext());
}

} // namespace content
